#include "Chromosome.h"
#include "CommandLineParmas.h"
#include "CSV.h"
#include <string>
#include <iostream>
#include <fstream>
#include <algorithm>

class MainProcess {
public:
    void run(int argv, char **argc) {
        using namespace std;
        CommandLineParmas commandLineParmas(argv, argc);
        if (1 == commandLineParmas.getParamsCount() || commandLineParmas.paramExists("-h")) {
            printHelpMessage();
            return;
        }
        string inputFile = commandLineParmas.getParam("-i");
        if (inputFile.empty()) {
            cout << "缺少参数或参数内容为空：-i。" << endl;
            printHelpMessage();
            return;
        }
        auto inputFileStream = new ifstream(inputFile);
        if (!inputFileStream->is_open()) {
            cout << "-i指定的文件不存在或无法读取。" << endl;
            delete inputFileStream;
            printHelpMessage();
            return;
        }
        cout << "-i:" << inputFile << endl;
        string errorFile = commandLineParmas.getParam("-o");
        if (errorFile.empty()) {
            cout << "缺少参数或参数内容为空：-o。" << endl;
            printHelpMessage();
            return;
        }
        cout << "-o:" << errorFile << endl;
        int pValueNumber = getIntegerExistsGTZero(commandLineParmas, "-p");
        if (0 == pValueNumber) {
            return;
        }
        cout << "-p:" << pValueNumber << endl;
        int lValueNumber = getIntegerExistsGTZero(commandLineParmas, "-l");
        if (0 == lValueNumber) {
            return;
        }
        cout << "-l:" << lValueNumber << endl;
        int sValueNumber = getIntegerExistsGTZero(commandLineParmas, "-s");
        if (0 == sValueNumber) {
            return;
        }
        cout << "-s:" << sValueNumber << endl;
        int kValueNumber = getIntegerExistsGTZero(commandLineParmas, "-k");
        if (0 == kValueNumber) {
            return;
        }
        cout << "-k:" << kValueNumber << endl;
        if (kValueNumber >= sValueNumber) {
            cout << "-k参数的值必须小于-s参数的值。" << endl;
            printHelpMessage();
            return;
        }
        double aValueNumber = getDoubleExistsGTZero(commandLineParmas, "-a");
        if (0.0 > aValueNumber) {
            return;
        }
        if (1.0 < aValueNumber) {
            aValueNumber = 1.0;
        }
        cout << "-a:" << aValueNumber << endl;
        double fValueNumber = getDoubleExistsGTZero(commandLineParmas, "-f");
        if (0.0 > fValueNumber) {
            return;
        }
        cout << "-f:" << fValueNumber << endl;
        cout << "============================================================" << endl;
        auto csv = new CSV(inputFile.c_str());
        testDataTotal = csv->getSize();
        testData = new double[(unsigned long)testDataTotal];
        for (int i = 0; i < testDataTotal; i++) {
            testData[i] = (*csv)[i];
        }
        delete csv;
        Chromosome::length = pValueNumber;

        int popSize = sValueNumber;
        int maxLoop = lValueNumber;
        int kill = kValueNumber;
        double vp = aValueNumber;
        double maxFitness = fValueNumber;
        auto randomNumberGenerator = RandomNumberGenerator::getRandomNumberGenerator();
        double lastMaxFitness = 0;
        Chromosome** pop = new Chromosome*[popSize];
        Chromosome *a, *b;
        double avg, var, var0 = 0;
        double p, survivorFitnessTotal, temp;
        for (int i = 0; i < popSize; i++) {
            pop[i] = Chromosome::generateRandomChromosome();
        }
        for (int loop = 0; loop < maxLoop; loop++) {
            sort(
                &(pop[0]),
                &(pop[popSize]),
                [](Chromosome* a, Chromosome* b) {
                    return a->getFitness() > b->getFitness();
                }
            );
            if (pop[0]->getFitness() > lastMaxFitness) {
                lastMaxFitness = pop[0]->getFitness();
                pop[0]->createRemanentErrorFile(errorFile);
            }
            avg = 0;
            for (int i = 0; i < popSize; i++) {
                avg += pop[i]->getFitness();
            }
            avg /= popSize;
            var = 0;
            for (int i = 0; i < popSize; i++) {
                var += (pop[i]->getFitness() - avg) * (pop[i]->getFitness() - avg);
            }
            var /= popSize;
            if (0 == loop) {
                var0 = var;
            }
            cout << "g," << (loop + 1) << ",fitness," << pop[0]->getFitness() << ",avg," << avg << ",var," << (var / var0) << endl;
            if (pop[0]->getFitness() >= maxFitness) {
                break;
            }
            survivorFitnessTotal = 0;
            for (int i = 0; i < popSize - kill; i++) {
                survivorFitnessTotal += pop[i]->getFitness();
            }
            for (int i = popSize - kill; i < popSize; i++) {
                delete pop[i];
                p = randomNumberGenerator->getRandomNumber(0.0, survivorFitnessTotal);
                temp = 0;
                a = pop[popSize - kill - 1];
                for (int j = 0; j < popSize - kill; j++) {
                    if (p <= temp) {
                        a = pop[j];
                        break;
                    }
                    temp += pop[j]->getFitness();
                }
                p = randomNumberGenerator->getRandomNumber(0.0, survivorFitnessTotal);
                temp = 0;
                b = pop[popSize - kill - 1];
                for (int j = 0; j < popSize - kill; j++) {
                    if (p <= temp) {
                        b = pop[j];
                        break;
                    }
                    temp += pop[j]->getFitness();
                }
                pop[i] = a->cross(b);
                pop[i]->varies(vp);
            }
        }
        cout << "============================================================" << endl;
        pop[0]->dump();
        cout << "SE:" << pop[0]->getStdError() << endl;
    }
private:
    void printHelpMessage() {
        using namespace std;
        cout << "参数说明：" << endl;
        cout << "-h 输出使用说明" << endl;
        cout << "-i 输入的CSV文件位置（必填）" << endl;
        cout << "-o 保留最近最好的参数拟合剩余的残差文件（必填）" << endl;
        cout << "-p AR模型的阶数（必填）" << endl;
        cout << "-l 最大迭代次数（必填）" << endl;
        cout << "-s 种群大小（必填）" << endl;
        cout << "-k 每次迭代去除多少个劣质解（必填）" << endl;
        cout << "-a 突变概率（必填）" << endl;
        cout << "-f 停止迭代适应度（必填）" << endl;
        cout << "使用示例：" << endl;
        cout << "./saar -i files/input.csv -o files/ar_error.csv -p 3 -l 10 -s 1000 -k 20 -a 0.01 -f 0.99" << endl;
        cout << endl;
    }
    int getIntegerExistsGTZero(CommandLineParmas& commandLineParmas, const char* param) {
        using namespace std;
        string valueString = commandLineParmas.getParam(param);
        int valueNumber = 0;
        if (valueString.empty()) {
            cout << "缺少参数或参数内容为空：" << param << "。" << endl;
            printHelpMessage();
            return 0;
        }
        try {
            valueNumber = stoi(valueString);
        } catch (invalid_argument& e) {
            cout << param << "参数的值有误。" << endl;
            printHelpMessage();
            return 0;
        } catch (out_of_range &e) {
            cout << param << "参数的值超出可接受的范围。" << endl;
            printHelpMessage();
            return 0;
        }
        if (valueNumber < 1) {
            cout << param << "参数的值必须大于或等于1。" << endl;
            printHelpMessage();
            return 0;
        }
        return valueNumber;
    }
    double getDoubleExistsGTZero(CommandLineParmas& commandLineParmas, const char* param) {
        using namespace std;
        string valueString = commandLineParmas.getParam(param);
        double valueNumber = 0;
        if (valueString.empty()) {
            cout << "缺少参数或参数内容为空：" << param << "。" << endl;
            printHelpMessage();
            return -1;
        }
        try {
            valueNumber = stod(valueString);
        } catch (invalid_argument& e) {
            cout << param << "参数的值有误。" << endl;
            printHelpMessage();
            return -1;
        } catch (out_of_range &e) {
            cout << param << "参数的值超出可接受的范围。" << endl;
            printHelpMessage();
            return -1;
        }
        if (valueNumber < 0) {
            cout << param << "参数的值必须大于或等于0。" << endl;
            printHelpMessage();
            return -1;
        }
        return valueNumber;
    }
};

int main(int argv, char **argc) {
    MainProcess program;
    program.run(argv, argc);
    return 0;
}
